import log from '../log.js';
// import OHIFError from './OHIFError';
import cornerstone from 'cornerstone-core';
import cornerstoneTools from 'cs-cornerstone-tools';
import StackManager from '../utils/StackManager.js';

export class StudyPrefetcher {
  static prefetchMethodMap = {
    topdown: 'getFirstDisplaySets',
    downward: 'getNextDisplaySets',
    closest: 'getClosestDisplaySets',
  };

  constructor(config = { order: 'closest', displaySetCount: 100 }) {
    this.config = config;
    this.displaySets = [];
    this.activeDisplaySet = null;
    this.prefetchDisplaySetsTimeout = 300;
    this.lastActiveViewportElement = null;

    cornerstone.events.addEventListener(
      'cornerstoneimagecachefull.StudyPrefetcher',
      this.cacheFullHandler
    );
  }

  destroy() {
    this.stopPrefetching();
    cornerstone.events.removeEventListener(
      'cornerstoneimagecachefull.StudyPrefetcher',
      this.cacheFullHandler
    );
  }

  static getInstance() {
    if (!StudyPrefetcher.instance) {
      StudyPrefetcher.instance = new StudyPrefetcher();
    }

    return StudyPrefetcher.instance;
  }

  setDisplaySets(displaySets, activeDisplaySet) {
    this.stopPrefetching();
    this.displaySets = displaySets;
    this.activeDisplaySet = activeDisplaySet;
  }

  prefetch() {
    if (!this.displaySets || !this.displaySets.length) {
      return;
    }

    this.stopPrefetching();
    this.prefetchDisplaySets();
  }

  stopPrefetching() {
    cornerstoneTools.requestPoolManager.clearRequestStack('thumbnail');
    cornerstoneTools.requestPoolManager.clearRequestStack('interaction');
    cornerstoneTools.requestPoolManager.clearRequestStack('prefetch');
  }

  prefetchDisplaySetsAsync(timeout) {
    timeout = timeout || this.prefetchDisplaySetsTimeout;

    clearTimeout(this.prefetchDisplaySetsHandler);
    this.prefetchDisplaySetsHandler = setTimeout(() => {
      this.prefetchDisplaySets();
    }, timeout);
  }

  prefetchDisplaySets() {
    const displaySetsToPrefetch = this.getDisplaySetsToPrefetch(config);
    const { thumbnailImageIds, activeImageIds, prefetchImageIds } = this.getImageIdsFromDisplaySets(displaySetsToPrefetch);

    this.prefetchImageIds(thumbnailImageIds, 'thumbnail');
    this.prefetchImageIds(activeImageIds, 'interaction');
    this.prefetchImageIds(prefetchImageIds);
  }

  prefetchImageIds(imageIds, requestType = 'prefetch') {
    const nonCachedImageIds = this.filterCachedImageIds(imageIds);
    const requestPoolManager = cornerstoneTools.requestPoolManager;
    const preventCache = false;
    const noop = () => { };

    nonCachedImageIds.forEach(imageId => {
      requestPoolManager.addRequest(
        {},
        imageId,
        requestType,
        preventCache,
        noop,
        noop
      );
    });

    requestPoolManager.startGrabbing();
  }

  getDisplaySetsToPrefetch() {
    const prefetchOrder = this.config.order;
    const methodName = StudyPrefetcher.prefetchMethodMap[prefetchOrder];
    const getDisplaySets = this[methodName];

    if (!getDisplaySets) {
      if (prefetchOrder) {
        log.warn(`Invalid prefetch order configuration (${prefetchOrder})`);
      }

      return [];
    }

    return getDisplaySets.call(this);
  }

  getFirstDisplaySets() {
    const length = this.displaySets.length;
    let count = this.config.displaySetCount;
    const selectedDisplaySets = [];

    for (let i = 0; i < length && count; i++) {
      const displaySet = this.displaySets[i];

      if (displaySet !== this.activeDisplaySet) {
        selectedDisplaySets.push(displaySet);
        count--;
      }
    }

    return selectedDisplaySets;
  }

  getNextDisplaySets() {
    const activeDisplaySetIndex = this.displaySets.indexOf(
      this.activeDisplaySet
    );
    const begin = activeDisplaySetIndex + 1;
    const end = Math.min(
      begin + this.config.displaySetCount,
      this.displaySets.length
    );

    return this.displaySets.slice(begin, end);
  }

  getClosestDisplaySets() {
    const activeDisplaySetIndex = this.displaySets.indexOf(
      this.activeDisplaySet
    );
    const length = this.displaySets.length;
    const selectedDisplaySets = [];
    let left = activeDisplaySetIndex - 1;
    let right = activeDisplaySetIndex + 1;
    let count = this.config.displaySetCount;

    while ((left >= 0 || right < length) && count) {
      if (left >= 0) {
        selectedDisplaySets.push(this.displaySets[left]);
        count--;
        left--;
      }

      if (right < length && count) {
        selectedDisplaySets.push(this.displaySets[right]);
        count--;
        right++;
      }
    }

    return selectedDisplaySets;
  }

  getImageIdsFromDisplaySets(displaySets) {
    let thumbnailImageIds = [];
    let prefetchImageIds = [];
    let activeImageIds = [];

    if (this.activeDisplaySet) {
      const activeStack = StackManager.findOrCreateStack(null, this.activeDisplaySet);
      if (activeStack.imageIds && activeStack.imageIds.length > 0) {
        thumbnailImageIds.push(activeStack.imageIds[0]);
        activeImageIds = activeStack.imageIds.slice(1);
      }
    }

    let hasEmpty = false;
    displaySets.forEach(displaySet => {
      if (hasEmpty) return;
      const stack = StackManager.findOrCreateStack(null, displaySet);
      if (stack.imageIds && stack.imageIds.length > 0) {
        thumbnailImageIds.push(stack.imageIds[0]);
        prefetchImageIds = prefetchImageIds.concat(stack.imageIds.slice(1));
      } else {
        prefetchImageIds = [];
        activeImageIds = [];
        hasEmpty = true;
      }
    });

    return { thumbnailImageIds, activeImageIds, prefetchImageIds };
  }

  filterCachedImageIds(imageIds) {
    return imageIds.filter(imageId => !this.isImageCached(imageId));
  }

  isImageCached(imageId) {
    const image = cornerstone.imageCache.imageCache[imageId];
    return image && image.sizeInBytes;
  }

  cacheFullHandler = () => {
    // log.warn('Cache full');
    // this.stopPrefetching();
  };
}
